package fr.ece.ing4.si.MonteCarlo.Calculation.MultiThreadedVersion;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import fr.ece.ing4.si.MonteCarlo.Calculation.*;

public class MonteCarloThreadedSimulation extends MonteCarloSimulation
{
	public int poolSize=0;
	public int numberOfCores=0;
	public double blockingCoefficient=0;
	
	/**
	 * Constructor. CPU info & poolSize.
	 */
	public MonteCarloThreadedSimulation()
	{
		numberOfCores = Runtime.getRuntime().availableProcessors();
		blockingCoefficient = 0.9;
		poolSize = (int) (numberOfCores / (1 - blockingCoefficient));
	}
	
	/**
	 * Second constructor. CPU info & the pool size is imposed.
	 * @param thePoolSize
	 */
	public MonteCarloThreadedSimulation(int thePoolSize)
	{
		numberOfCores = Runtime.getRuntime().availableProcessors();
		poolSize = thePoolSize;
	}
	
	/**
	 * Multi-threading calculation of the simulations.
	 */
	public double Simulation(final String CallPutFlag, final double S, final double X,
			final double T, double r, final double b, final double v, final int nSteps, int nSimulations) throws InterruptedException, ExecutionException
		{
			int numberOfCores = Runtime.getRuntime().availableProcessors();
			System.out.println("Number of Cores available is " + numberOfCores);
			System.out.println("Pool size is " + poolSize);
		
			double Sum=0;	
			
			List<Callable<Double>> simulations = new ArrayList<Callable<Double>>();
			
			for(int i=1; i<nSimulations; i++)
			{
				simulations.add(
						new Callable<Double>()
						{
							public Double call() throws Exception
							{
								return MonteCarloSimulation.Calcul(CallPutFlag, nSteps, S, X, T, b, v);
							}
						});
			}
			
			ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);
			List<Future<Double>> valueOfSimulations = executorPool.invokeAll(simulations,10000, TimeUnit.SECONDS);
			executorPool.shutdown();

			for (Future<Double> valueOfASimulation : valueOfSimulations)
			{
				Sum = Sum + valueOfASimulation.get();
			}
			
			//System.out.println("Sum: " + Math.exp(-r * T) * (Sum / nSimulations));
			return Math.exp(-r * T) * (Sum / nSimulations);
		}
	
	public static void main(final String[] args) throws InterruptedException, ExecutionException
	{
		new MonteCarloThreadedSimulation().timeAndComputeValue("c",40,50,0.5,0.06,0.1,0.45,168,100);
	}
}
